Skip to content

Feat/zpl mu units of measure - #128

Merged
u8array merged 5 commits into
mainfrom
feat/zpl-mu-units-of-measure
Jun 4, 2026
Merged

u8array merged 5 commits into
mainfrom
feat/zpl-mu-units-of-measure

Conversation

@u8array

@u8array u8array commented Jun 4, 2026

Copy link
Copy Markdown
Owner

No description provided.

u8array added 4 commits June 4, 2026 09:05
Parser normalizes ^MUI/^MUM coords to dots at read-time via a new
intDots/intDotsOrUndef helper threaded through ~30 dot-quantity sites
(FO/FT, GB/GC/GD/GE/GS, A/CF/FB/FP/LH/LT/TB, BY + all bcHeight, PW/LL/
LS/ML). intDots uses parseFloat so fractional inch/mm values (`^MUI^FO
0.5,0.5`) survive instead of getting parseInt-truncated to zero.

^MU `a`-slot scope follows the spec: carries field-by-field until
overridden, no ^XA reset. Per-block isolation in production comes from
zplImportService calling parseZPL per ^XA block. b/c slots persist on
labelConfig.formatDpi/outputDpi and re-emit as `^MUD,b,c` only when
both are set (paired directive; no synthesized half-set ratio).

Invalid `a` (e.g. `^MUX`) and out-of-spec b/c surface as a partial
import finding instead of silently normalizing.
Curried dot-helper plumbing:
- helpers.ts gains `dotsFor(state)` which binds intDots/intDotsOrUndef
  to a state's live unitScale. Replaces a per-factory closure that
  was duplicated across fields/barcodes/graphics/labelConfig.
- intDots collapses to a thin fallback wrapper over intDotsOrUndef
  (DRY: one parseFloat+NaN+round body, not two).

Factory-signature unification:
- createSetupScriptHandlers(s), createLabelConfigHandlers(s, dpmm) and
  createBarcodeHandlers(s) now take ParserState, matching the existing
  createFieldHandlers/createGraphicsHandlers pattern.
- ^A dynamic-font dispatch fallback moves out of zplParser.ts into a
  named export `handleDynamicFontA` in fields.ts. Orchestrator still
  owns the `cmd[0]==='A'` wildcard match.
- New units.ts handler factory owns the ^MU command (both unitScale
  format-state mutation and muResampling labelConfig persistence).

Schema reshape:
- formatDpi/outputDpi individual optionals → `muResampling: { formatDpi,
  outputDpi } | undefined` paired sub-object. Type-enforces the
  spec's "both or neither" invariant; parser persists only when both
  slots arrive valid, generator emits the pair without half-set guards.

Correctness:
- Invalid ^MU a-slot (e.g. ^MUX) preserves prior unitScale instead
  of silently downgrading to dots. Marked partial.

FormatState.unitScale documented as the one slot that intentionally
survives ^XA per ^MU spec; per-block isolation comes from
zplImportService calling parseZPL per ^XA block.
The ^A{font} dynamic-font command can't live in the exact-match
handler table because the font char is part of the command key.
Previously it leaked through as a named-export helper plus an
orchestrator-side inline `cmd[0]==='A' && cmd.length===2` check.

Introduces a `Wildcard` type (matches + handle) so the pattern-match
logic stays with its owning handler module. `createDynamicFontAWildcard`
in fields.ts now declares the pattern; the orchestrator collects
wildcards into a list and falls back to them only after exact-match
dispatch fails, so a registered handler always wins.

Handler signature gains `cmd: string` as a third argument so wildcards
can inspect the matched command key (e.g. read the font char from
`cmd[1]`). Existing handlers ignore the new arg.

Removes the named-export workaround; the orchestrator no longer needs
to know about specific wildcard patterns.
^BR p[2] is a 1-10 magnification multiplier, not a dot quantity. The
parser previously wrote it into `defaults.byModuleWidth` (the shared
^BY slot, dot-typed everywhere else), so a ^BR field would silently
overwrite real dot widths from ^BY and leak the magnification value
into subsequent non-gs1 barcodes.

Parser side:
- `FieldState.gsMagnification` (1-10 or undefined) carries ^BR p[2]
  on its own slot. Reset at ^FS so a bare ^BR^FS doesn't leak.
- Out-of-range values fall back to `defaults.byModuleWidth` at flush
  time, preserving the prior "single sizing knob" semantics when ^BY
  and ^BR carry the same value (the common case).

Schema rename:
- `Gs1DatabarProps.moduleWidth` → `magnification`. The value always
  represented the ^BR multiplier semantically; the rename ends the
  confusion with the dot-typed ^BY value of the same name.
- Generator emits `magnification` to both ^BY and ^BR slots per the
  Zebra convention. Imports with mismatched ^BY/^BR values normalize
  to the ^BR slot (firmware behavior), so `^BY4 ^BR_,_,6` re-emits
  as `^BY6 ^BR_,_,6`.

Schema migration:
- designFile.parseDesignFile and labelStore persist (v5→v6) walk the
  object tree and rename `props.moduleWidth` → `props.magnification`
  for gs1databar leaves. Both routes share a new `objectTree.ts`
  walker. Migration is idempotent and preserves an existing
  magnification key.

Locale rename:
- `registry.gs1databar.moduleWidth` → `registry.gs1databar.magnification`
  across 32 locales. Display value seeded from aztec/qrcode.magnification
  ("Size (1-10)" in en) so the user-facing label matches how the rest
  of the registry names the same 1-10 knob.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for the ZPL ^MU (Units of Measure) command and refactors gs1databar barcode properties by renaming moduleWidth to magnification to clarify its role as a multiplier. It also introduces migration utilities to seamlessly upgrade legacy design files. The review feedback highlights two critical TypeScript compilation and runtime errors where the non-existent partialCmds property is accessed on s.result in both units.ts and fields.ts. Additionally, a medium-severity issue was identified in objectTree.ts where arrays could bypass type checks and be passed to the leaf visitor, which can be resolved by adding an explicit Array.isArray check.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib/zplParser/handlers/units.ts
Comment thread src/lib/zplParser/handlers/fields.ts
Comment thread src/lib/objectTree.ts Outdated
Defensive: arrays satisfy `typeof node === "object"` so a caller
passing an array directly would have fallen through to `visit(n)`
with the array itself. Current callers iterate before recursing, so
this is theoretical, but the guard removes the surprise.
@u8array
u8array merged commit f48e883 into main Jun 4, 2026
2 checks passed
@u8array
u8array deleted the feat/zpl-mu-units-of-measure branch June 5, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant